﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace Groovy_IM.Protocols.MySpace
{
    public class Sock
    {
        private Socket m_Socket;
        public bool isConnected;
        private string Server;
        private int serverPort;
        private const int bufferSize = 2048;

        public delegate void Closed();
        public delegate void Connected(bool Connected);
        public delegate void OnArrival(string Message);

        public event Closed OnClose;
        public event Connected OnConnect;
        public event OnArrival DataArrival;

        public void Close()
        {
            if (!isConnected)
                return;

            isConnected = false;

            m_Socket.Close();

            m_Socket = null;

            if (OnClose != null)
                OnClose();
        }

        public void CreateConnection(string serverAddress, int port)
        {
            this.m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            this.Server = serverAddress;
            this.serverPort = port;

            var connectionOperation = new SocketAsyncEventArgs { RemoteEndPoint = new DnsEndPoint(this.Server, this.serverPort) };
            connectionOperation.Completed += OnConnectionToServerCompleted;

            this.m_Socket.ConnectAsync(connectionOperation);
        }

        public void SendToServer(string message)
        {
            var asyncEvent = new SocketAsyncEventArgs { RemoteEndPoint = new DnsEndPoint(Server, serverPort) };

            var buffer = Encoding.UTF8.GetBytes(message + Environment.NewLine);
            asyncEvent.SetBuffer(buffer, 0, buffer.Length);

            m_Socket.SendAsync(asyncEvent);
        }

        private void OnConnectionToServerCompleted(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError != SocketError.Success)
            {
                if (OnConnect != null)
                    OnConnect(false);

                isConnected = false;

                return;
            }

            if (OnConnect != null)
                OnConnect(true);

            ReceiveMessage();
        }

        private void ReceiveMessage()
        {
            var responseListener = new SocketAsyncEventArgs();
            responseListener.Completed += OnMessageReceivedFromServer;

            var responseBuffer = new byte[bufferSize];
            responseListener.SetBuffer(responseBuffer, 0, bufferSize);

            m_Socket.ReceiveAsync(responseListener);
        }

        private string trailingMessage;
        private void OnMessageReceivedFromServer(object sender, SocketAsyncEventArgs e)
        {
            var message = Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred);

            var bufferWasPreviouslyFull = !string.IsNullOrWhiteSpace(trailingMessage);
            if (bufferWasPreviouslyFull)
            {
                message = trailingMessage + message;
                trailingMessage = null;
            }

            var isConnectionLost = string.IsNullOrWhiteSpace(message);
            if (isConnectionLost)
            {
                if (OnClose != null)
                    OnClose();

                isConnected = false;
                return;
            }

            var lines = new List<string>(message.Split("\n\r".ToCharArray(), StringSplitOptions.None));

            var lastLine = lines.LastOrDefault();
            
            var isBufferFull = !string.IsNullOrWhiteSpace(lastLine);
            
            if (isBufferFull)
            {
                trailingMessage = lastLine;
                lines.Remove(lastLine);
            }

            foreach (var line in lines)
            {
                if (string.IsNullOrWhiteSpace(line))
                    continue;

                ProcessIncomingMessage(line);
            }

            ReceiveMessage();
        }

        private void ProcessIncomingMessage(string ircMessage)
        {
            if (DataArrival != null)
                DataArrival(ircMessage);
        }
    }
}